package com.yzg.unsafe;

import com.yzg.cas.CasVariable;
import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * @author misterWei
 * @create 2020年05月31号:00点43分
 * @mailbox forcontinue@163.com
 * 自定义实现 AtomicInteger 类似原子操作
 */
@Slf4j(topic = "c.AtomicUnsafe")
public class AtomicUnsafe implements CasVariable.Account {
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof AtomicUnsafe)) return false;
        AtomicUnsafe that = (AtomicUnsafe) o;
        return value == that.value;
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }


    public AtomicUnsafe(int value) {
        this.value = value;
    }

    private static final long offset;
    private volatile int value;
    private static final Unsafe UNSAFE;
    // 获取 unsafe 对象的方式
    //1 参考其他源码,次方法仅限于制定的security 使用
    //private static final Unsafe UNSAFE = Unsafe.getUnsafe();
/*    //2 比较规范
    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get(null);
        } catch (NoSuchFieldException   | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }*/

    static {
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            UNSAFE = (Unsafe) theUnsafe.get(null);
            //获取偏移量
          offset =  UNSAFE.objectFieldOffset(AtomicUnsafe.class.getDeclaredField("value"));
        } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public int get(){
        return value;
    }

    @Override
    public void optionAccount(int price){

        while (true){
            int perv = get();
            int next = perv - price;
            if (UNSAFE.compareAndSwapInt(this,offset,perv,next)) {
                break;
            }
        }
    }

    @Override
    public int getAccount() {
        return get();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            CasVariable.Account  account = new AtomicUnsafe(10000);
            account.test(account);
            System.out.println(account.getAccount());
        }


    }
}
